﻿using System;
using System.Collections.Generic;
using System.Threading;
using Netbits.Common.Conditions;
using Netbits.Common.Threading;

namespace Netbits.Common.Collections
{
	/// <summary>
	/// Represent a typed extended list with event handling and extended IEnumerable
	/// </summary>
	/// <typeparam name="TItem">Strongly typed items</typeparam>
	public class ListExtended<TItem> : IList<TItem>, IExtended<ListExtended<TItem>, TItem>, IDisposable
	{
		#region Private Variables

		bool _Disposed;

		/// <summary>
		/// Holds all TItems.
		/// </summary>
		protected readonly List<TItem> _Items;

		/// <summary>
		/// Should the events send asynchronous
		/// </summary>
		protected bool _UseAyncEvents;
		/// <summary>
		/// Wait handler for wait if items was added to list.
		/// </summary>
		protected System.Threading.ManualResetEventSlim _WaitForItems;

		/// <summary>
		/// Fired before an item was added
		/// </summary>
		protected SaveEventHandler<ListExtended<TItem>, TItem> _EventBeforeAddItem = new SaveEventHandler<ListExtended<TItem>,TItem>();
		/// <summary>
		/// Fired after an item was added
		/// </summary>
		protected SaveEventHandler<ListExtended<TItem>, TItem> _EventAfterAddItem = new SaveEventHandler<ListExtended<TItem>,TItem>();
		/// <summary>
		/// Fired before an item was insert on index position
		/// </summary>
		protected SaveEventHandler<ListExtended<TItem>, int, TItem> _EventBeforeInsertItem = new SaveEventHandler<ListExtended<TItem>,int,TItem>();
		/// <summary>
		/// Fired after an item was insert on index position
		/// </summary>
		protected SaveEventHandler<ListExtended<TItem>, int, TItem> _EventAfterInsertItem = new SaveEventHandler<ListExtended<TItem>,int,TItem>();
		/// <summary>
		/// Fired before an item was removed
		/// </summary>
		protected SaveEventHandler<ListExtended<TItem>, TItem> _EventBeforeRemoveItem = new SaveEventHandler<ListExtended<TItem>,TItem>();
		/// <summary>
		/// Fired after an item was removed
		/// </summary>
		protected SaveEventHandler<ListExtended<TItem>, TItem> _EventAfterRemoveItem = new SaveEventHandler<ListExtended<TItem>,TItem>();
		/// <summary>
		/// Fired before an item was removed on specified index position
		/// </summary>
		protected SaveEventHandler<ListExtended<TItem>, int, TItem> _EventBeforeRemoveItemAt = new SaveEventHandler<ListExtended<TItem>,int,TItem>();
		/// <summary>
		/// Fired after an item was removed on specified index position
		/// </summary>
		protected SaveEventHandler<ListExtended<TItem>, int, TItem> _EventAfterRemoveItemAt = new SaveEventHandler<ListExtended<TItem>,int,TItem>();
		/// <summary>
		/// Fired before an item was modified
		/// </summary>
		protected SaveEventHandler<ListExtended<TItem>, TItem> _EventBeforeModifyItem = new SaveEventHandler<ListExtended<TItem>,TItem>();
		/// <summary>
		/// Fired after an item was modified
		/// </summary>
		protected SaveEventHandler<ListExtended<TItem>, TItem> _EventAfterModifyItem = new SaveEventHandler<ListExtended<TItem>,TItem>();
		/// <summary>
		/// Fired before the list clears all items
		/// </summary>
		protected SaveEventHandler<ListExtended<TItem>> _EventBeforeClearItems = new SaveEventHandler<ListExtended<TItem>>();
		/// <summary>
		/// Fired after the list is clear
		/// </summary>
		protected SaveEventHandler<ListExtended<TItem>> _EventAfterClearItems = new SaveEventHandler<ListExtended<TItem>>();
		/// <summary>
		/// Holds the maximum of items that the list can contain
		/// -1 for unlimited items.
		/// </summary>
		protected int _MaxItems = -1;

		#endregion

		#region Constructors

		/// <summary>
		/// Create a new instance of ListExtended
		/// </summary>
		public ListExtended()
		{
			_Items = new List<TItem>();
			_WaitForItems = new System.Threading.ManualResetEventSlim();
		}

		/// <summary>
		/// Create a new instance of ListExtended with the specified initial capacity
		/// </summary>
		/// <param name="Capacity_">The number of items that the list can initially store</param>
		public ListExtended( int Capacity_ )
		{
			_Items = new List<TItem>( Capacity_ );
			_WaitForItems = new System.Threading.ManualResetEventSlim();
		}

		/// <summary>
		/// Create a new instance of ListExtended that contains items copied from the specified collection.
		/// </summary>
		/// <param name="Collection_">The collection whose items are copied</param>
		public ListExtended( IEnumerable<TItem> Collection_ )
		{
			_Items = new List<TItem>( Collection_ );
			_WaitForItems = new System.Threading.ManualResetEventSlim();
		}

		#endregion

		#region IList

		/// <summary>
		/// Get the index of the specified item
		/// </summary>
		/// <remarks>NULL items are not allowed! It throws an ArgumentNullException</remarks>
		/// <param name="Item_">The item to locate in the list</param>
		/// <returns>The index of the first occurrence of the item, otherwise -1</returns>
		public virtual int IndexOf( TItem Item_ )
		{
			Condition.Requires( Item_, "Item_" ).IsNotNull();
			return _Items.IndexOf( Item_ );
		}

		/// <summary>
		/// Insert new Item to the list at the specified index.
		/// </summary>
		/// <remarks>NULL items are not allowed! It throws an ArgumentNullException</remarks>
		/// <param name="Index_">The index at which item should be inserted</param>
		/// <param name="Item_">The item to insert</param>
		public void Insert( int Index_, TItem Item_ )
		{
			Condition.Requires( Item_ ).IsNotNull();
			__CheckIndex( Index_ );

			_EventBeforeInsertItem.Notify( this, Index_, Item_ );
			__InsertItem( Index_, Item_ );
			_EventAfterInsertItem.Notify( this, Index_, Item_ );
		}

		/// <summary>
		/// Remove the item at the specified index-position
		/// </summary>
		/// <param name="Index_">The index of the item to remove</param>
		public void RemoveAt( int Index_ )
		{
			__CheckIndex( Index_ );

			TItem item = __GetItem( Index_ );

			_EventBeforeRemoveItemAt.Notify( this, Index_, item );
			__RemoveAtItem( Index_ );
			_EventAfterRemoveItemAt.Notify( this, Index_, item );
		}

		/// <summary>
		/// Get or set the item at the specified index-position
		/// </summary>
		/// <param name="Index_">The index of the item to get or set</param>
		/// <returns>The item at the specified index-position, otherwise IndexOutOfRangeException</returns>
		public TItem this[int Index_]
		{
			get
			{
				__CheckIndex( Index_ );
				return __GetItem( Index_ );
			}
			set
			{
				Condition.Requires( value ).IsNotNull();
				__CheckIndex( Index_ );

				TItem item = __GetItem( Index_ );

				_EventBeforeModifyItem.Notify( this, item );
				__SetItem( Index_, value );
				_EventAfterModifyItem.Notify( this, item );
			}
		}

		/// <summary>
		/// Add the item to end of the list.
		/// </summary>
		/// <param name="Item_">The item to added to the end of the list</param>
		public void Add( TItem Item_ )
		{
			Condition.Requires( Item_ ).IsNotNull();

			_EventBeforeAddItem.Notify( this, Item_ );
			__AddItem( Item_ );
			_EventAfterAddItem.Notify( this, Item_ );
		}

		/// <summary>
		/// Clear all items from the list.
		/// </summary>
		public void Clear()
		{
			_EventBeforeClearItems.Notify( this );
			__ClearItems();
			_EventAfterClearItems.Notify( this );
		}

		/// <summary>
		/// Determines whether the list contains the specified item.
		/// </summary>
		/// <param name="Item_">The item to locate in the list</param>
		/// <returns>true if the item is found in the list, otherwise false</returns>
		public virtual bool Contains( TItem Item_ )
		{
			return _Items.Contains( Item_ );
		}

		/// <summary>
		/// Copies the entire list to a compatible one-dimensional array, starting at the specified index of the target array.
		/// </summary>
		/// <param name="Array_">The one-dimensional array that is the destination of the items copied from the list.</param>
		/// <param name="ArrayIndex_">The index in array at which copying begins</param>
		public virtual void CopyTo( TItem[] Array_, int ArrayIndex_ )
		{
			Condition.Requires( Array_, "Array_" ).IsNotNull();
			Condition.Requires( ArrayIndex_, "ArrayIndex_" ).IsInRange( 0, Array_.Length );

			_Items.CopyTo( Array_, ArrayIndex_ );
		}

		/// <summary>
		/// Get the number of items actually contained in the list.
		/// </summary>
		public virtual int Count
		{
			get
			{
				return _Items.Count;
			}
		}

		/// <summary>
		/// Gets a value indicating whether the list is read-only. 
		/// In this implementation this property always return false.
		/// </summary>
		public virtual bool IsReadOnly
		{
			get
			{
				return false;
			}
		}

		/// <summary>
		/// Remove the first occurrence of the specified item from the list.
		/// </summary>
		/// <param name="Item_">The item to remove from the list</param>
		/// <returns>true if the item was successfully removed, otherwise false</returns>
		public bool Remove( TItem Item_ )
		{
			Condition.Requires( Item_ ).IsNotNull();

			bool ret;

			_EventBeforeRemoveItem.Notify( this, Item_ );
			ret = __RemoveItem( Item_ );
			_EventAfterRemoveItem.Notify( this, Item_ );
			
			return ret;
		}

		/// <summary>
		/// Get the enumerator that iterates through the list
		/// </summary>
		/// <returns>The new enumerator</returns>
		public virtual IEnumerator<TItem> GetEnumerator()
		{
			return _Items.GetEnumerator();
		}

		/// <summary>
		/// Get the enumerator that iterates through the list
		/// </summary>
		/// <returns>The new enumerator</returns>
		System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
		{
			return _Items.GetEnumerator();
		}

		#endregion

		#region ICollectionEvents

		/// <summary>
		/// Occurs before an item is added to list
		/// </summary>
		public SaveEventHandler<ListExtended<TItem>, TItem> BeforeAddItem
		{
			get
			{
				return _EventBeforeAddItem;
			}
		}

		/// <summary>
		/// Occurs after an item was added to list
		/// </summary>
		public SaveEventHandler<ListExtended<TItem>, TItem> AfterAddItem
		{
			get
			{
				return _EventAfterAddItem;
			}
		}

		/// <summary>
		/// Occurs before an item is insert on specified index-position.
		/// </summary>
		public SaveEventHandler<ListExtended<TItem>, int, TItem> BeforeInsertItem
		{
			get
			{
				return _EventBeforeInsertItem;
			}
		}

		/// <summary>
		/// Occurs after an items was insert at specified index-position
		/// </summary>
		public SaveEventHandler<ListExtended<TItem>, int, TItem> AfterInsertItem
		{
			get
			{
				return _EventAfterInsertItem;
			}
		}

		/// <summary>
		/// Occurs before an item was removed from the list
		/// </summary>
		public SaveEventHandler<ListExtended<TItem>, TItem> BeforeRemoveItem
		{
			get
			{
				return _EventBeforeRemoveItem;
			}
		}

		/// <summary>
		/// Occurs after an item was removed from list.
		/// </summary>
		public SaveEventHandler<ListExtended<TItem>, TItem> AfterRemoveItem
		{
			get
			{
				return _EventAfterRemoveItem;
			}
		}

		/// <summary>
		/// Occurs before an item was removed from the list at specified index.
		/// </summary>
		public SaveEventHandler<ListExtended<TItem>, int, TItem> BeforeRemoveItemAt
		{
			get
			{
				return _EventBeforeRemoveItemAt;
			}
		}

		/// <summary>
		/// Occurs after an item was successfully removed from the list at the specified index
		/// </summary>
		public SaveEventHandler<ListExtended<TItem>, int, TItem> AfterRemoveItemAt
		{
			get
			{
				return _EventAfterRemoveItemAt;
			}
		}

		/// <summary>
		/// Occurs before an item was modified 
		/// </summary>
		public SaveEventHandler<ListExtended<TItem>, TItem> BeforeModifyItem
		{
			get
			{
				return _EventBeforeModifyItem;
			}
		}

		/// <summary>
		/// Occurs after an item was modified
		/// </summary>
		public SaveEventHandler<ListExtended<TItem>, TItem> AfterModifyItem
		{
			get
			{
				return _EventAfterModifyItem;
			}
		}

		/// <summary>
		/// Occurs before the list was cleared
		/// </summary>
		public SaveEventHandler<ListExtended<TItem>> BeforeClearItems
		{
			get
			{
				return _EventBeforeClearItems;
			}
		}

		/// <summary>
		/// Occurs after the list is cleared
		/// </summary>
		public SaveEventHandler<ListExtended<TItem>> AfterClearItems
		{
			get
			{
				return _EventAfterClearItems;
			}
		}

		/// <summary>
		/// Get or set if the list use asynchrony events.
		/// </summary>
		public virtual bool UseAsyncEvents
		{
			get
			{
				return _UseAyncEvents;
			}
			set
			{
				_UseAyncEvents = value;
			}
		}

		/// <summary>
		/// After this function is called the calling thread was blocked until any item is in the list
		/// </summary>
		public void WaitForItems()
		{
			_WaitForItems.Wait();
		}

		/// <summary>
		/// After this function is called the calling thread was blocked until any item is in the list or the specified timeout was reached
		/// </summary>
		/// <param name="Timeout_">The specified timeout to wait</param>
		public bool WaitForItems( int Timeout_ )
		{
			return _WaitForItems.Wait( Timeout_ );
		}

		/// <summary>
		/// Get the Wait-Handler (ManualResetEventSlim)
		/// </summary>
		/// <returns>The Wait-Handler</returns>
		public ManualResetEventSlim WaitHandler()
		{
			return _WaitForItems;
		}

		#endregion

		#region IEnumerableExtended

		/// <summary>
		/// Get the first item of the list. 
		/// </summary>
		public virtual TItem First
		{
			get
			{
				Condition.Requires( this.Count ).IsGreaterThan( 0 );
				return this[0];
			}
		}

		/// <summary>
		/// Get the last item of the list
		/// </summary>
		public virtual TItem Last
		{
			get
			{
				Condition.Requires( this.Count ).IsGreaterThan( 0 );
				return this[this.Count - 1];
			}
		}

		#endregion

		#region Protected Functions

		/// <summary>
		/// Function that checks the index is in range of list.
		/// </summary>
		/// <param name="Index_">The index to check</param>
		protected virtual void __CheckIndex( int Index_ )
		{
			Condition.Requires( Index_ ).IsInRange( -1, _Items.Count );
		}

		/// <summary>
		/// Function that would called if the item at the index-position was changed
		/// </summary>
		/// <param name="Index_">The index of the item to be changed</param>
		/// <param name="Item_">The new item</param>
		protected virtual void __SetItem( int Index_, TItem Item_ )
		{
			_Items[Index_] = Item_;
		}

		/// <summary>
		/// Function that would called if an item was accessed per indexer
		/// </summary>
		/// <param name="Index_">The index of the item that was accessed</param>
		/// <returns>The Item as the specified index-position</returns>
		protected virtual TItem __GetItem( int Index_ )
		{
			return _Items[Index_];
		}

		/// <summary>
		/// Function that would called if an item would remove from index-position.
		/// </summary>
		/// <param name="Index_">The index of the item that would be removed from the list</param>
		protected virtual void __RemoveAtItem( int Index_ )
		{
			_Items.RemoveAt( Index_ );
			if( _Items.Count == 0 )
				_WaitForItems.Reset();
		}

		/// <summary>
		/// Function that would called if an item would remove from the list.
		/// </summary>
		/// <param name="Item_">The item that would remove from the list.</param>
		/// <returns>true if the item was successfully removed, otherwise false</returns>
		protected virtual bool __RemoveItem( TItem Item_ )
		{
			bool ret = _Items.Remove( Item_ );
			if( _Items.Count == 0 )
				_WaitForItems.Reset();
			return ret;

		}

		/// <summary>
		/// Function that would called if all items should be removed from the list.
		/// </summary>
		protected virtual void __ClearItems()
		{
			_Items.Clear();
			_WaitForItems.Reset();
		}

		/// <summary>
		/// Function that would called if an item should be insert at the index-position
		/// </summary>
		/// <param name="Index_">The index of the item should be insert.</param>
		/// <param name="Item_">The item that should be insert at the specified index position</param>
		protected virtual void __InsertItem( int Index_, TItem Item_ )
		{
			if( _MaxItems > 0 && _MaxItems == _Items.Count )
				_Items.Remove( _Items[0] );

			_Items.Insert( Index_, Item_ );
			_WaitForItems.Set();
		}

		/// <summary>
		/// Function that would called if an item should be added to list.
		/// </summary>
		/// <param name="Item_">The item that should be added to list.</param>
		protected virtual void __AddItem( TItem Item_ )
		{
			if( _MaxItems > 0 && _MaxItems == _Items.Count )
				_Items.Remove( _Items[0] );

			_Items.Add( Item_ );
			_WaitForItems.Set();
		}

		/// <summary>
		/// Dispose ListExtende Resources
		/// <param name="Disposing_">Dispose managed resources</param>
		/// </summary>
		protected virtual void Dispose( bool Disposing_ )
		{
			if( !_Disposed )
			{
				if( Disposing_ )
				{
					if( _WaitForItems != null )
						_WaitForItems.Dispose();

					if( _EventAfterAddItem != null )
						_EventAfterAddItem.Dispose();

					if( _EventAfterClearItems != null )
						_EventAfterClearItems.Dispose();

					if( _EventAfterInsertItem != null )
						_EventAfterInsertItem.Dispose();

					if( _EventAfterModifyItem != null )
						_EventAfterModifyItem.Dispose();

					if( _EventAfterRemoveItem != null )
						_EventAfterRemoveItem.Dispose();

					if( _EventAfterRemoveItemAt != null )
						_EventAfterRemoveItemAt.Dispose();

					if( _EventBeforeAddItem != null )
						_EventBeforeAddItem.Dispose();

					if( _EventBeforeClearItems != null )
						_EventBeforeClearItems.Dispose();

					if( _EventBeforeInsertItem != null )
						_EventBeforeInsertItem.Dispose();

					if( _EventBeforeModifyItem != null )
						_EventBeforeModifyItem.Dispose();

					if( _EventBeforeRemoveItem != null )
						_EventBeforeRemoveItem.Dispose();

					if( _EventBeforeRemoveItemAt != null )
						_EventBeforeRemoveItemAt.Dispose();
				}

				_Disposed = true;
			}
		}

		#endregion

		#region IExtended

		/// <summary>
		/// Get or sets the maximum items that the list can contain.
		/// -1 for unlimited items.
		/// </summary>
		public virtual int MaxItems
		{
			get
			{
				return _MaxItems;
			}
			set
			{
				_MaxItems = value;
			}
		}

		/// <summary>
		/// Creates a copy of all items in the list in a new List
		/// </summary>
		/// <returns>The new list</returns>
		public virtual List<TItem> CopyToNewList()
		{
			List<TItem> items = new List<TItem>( Count );
			foreach( TItem item in _Items )
				items.Add( item );
			return items;
		}

		#endregion

		#region IDisposabe

		/// <summary>
		/// Dispose ListExtended Resources
		/// </summary>
		public void Dispose()
		{
			Dispose( true );
			GC.SuppressFinalize( this );
		}

		#endregion
	}
}
